02 - Podstawy OpenCV
Wprowadzenie do przetwarzania obrazów
Politechnika Poznańska, Instytut Robotyki i Inteligencji Maszynowej
Ćwiczenie laboratoryjne 2: Podstawowe operacje na obrazach i elementy sterujące
Powrót do spisu treści ćwiczeń laboratoryjnych
Obsługa zdarzeń interfejsu - trackbar
Duża część funkcji dostępnych w OpenCV wymaga ustawienia parametrów, a ich wartość jest zależna od typu obrazów, na których wykonywane są operacje. Sposobem na ułatwienie dobrania wartości parametru jest wykorzystanie elementu graficznego interfejsu użytkownika o nazwie trackbar. Pozwala on kontrolować wartość wybranej zmiennej w programie, a tym samym na podgląd w czasie rzeczywistym efektu działania algorytmu w zależności od parametru.
Więcej o trackbarach możesz sprawdzić w tutorialu: https://docs.opencv.org/5.x/da/d6a/tutorial_trackbar.html.
import cv2
import numpy as np
def empty_callback(value):
pass
# create a black image, a window
= np.zeros((300, 512, 3), dtype=np.uint8)
img 'image')
cv2.namedWindow(
# create trackbars for color change
'R', 'image', 0, 255, empty_callback)
cv2.createTrackbar('G', 'image', 0, 255, empty_callback)
cv2.createTrackbar('B', 'image', 0, 255, empty_callback)
cv2.createTrackbar(
# create switch for ON/OFF functionality
= '0 : OFF \n1 : ON'
switch_trackbar_name 'image', 0, 1, empty_callback)
cv2.createTrackbar(switch_trackbar_name,
while True:
'image', img)
cv2.imshow(
# sleep for 10 ms waiting for user to press some key, return -1 on timeout
= cv2.waitKey(10)
key_code if key_code == 27:
# escape key pressed
break
# get current positions of four trackbars
= cv2.getTrackbarPos('R', 'image')
r = cv2.getTrackbarPos('G', 'image')
g = cv2.getTrackbarPos('B', 'image')
b = cv2.getTrackbarPos(switch_trackbar_name, 'image')
s
if s == 0:
# assign zeros to all pixels
= 0
img[:] else:
# assign the same BGR color to all pixels
= [b, g, r]
img[:]
# closes all windows (usually optional as the script ends anyway)
cv2.destroyAllWindows()
UWAGA
W przykładzie do wszystkich trackbarów podłączono tę samą funkcję empty_callback, która nie wykonuje żadnej operacji. Należy pamiętać, że funkcja ta obsługuje wywołanie zdarzenia od każdego trackbaru, w związku z czym w większości przypadków powinna być inna dla każdego elementu. Argument value, który przyjmuje, to wartość trackbaru. Prosty sposób prezentacji działania to dodanie do niej linijki:
print(f'Trackbar reporting for duty with value: {value}')
💥 Zadanie do wykonania 💥
Uruchom przykładowy, dostępny powyżej program. Zmodyfikuj funkcję empty_callback w taki sposób, aby wyświetlała zmienną value
w konsoli.
Operacje na pikselach - progowanie
W celu wykonania binaryzacji obrazu (zamiany do formatu, w którym występują tylko dwie wartości / kolory - biały / czarny) wykorzystuje się operację progowania. Zapoznać się z instrukcją: https://docs.opencv.org/5.x/d7/d4d/tutorial_py_thresholding.html.
UWAGA Trackbar należy podłączyć do okna, które istnieje w momencie tworzenia trackbaru. W celu utworzenia okna (bez podawania obrazu do wyświetlenia) skorzystać z funkcji
cv2.namedWindow(‘windowName’)
.
Zadania do wykonania (w podanej kolejności!):
- Wczytaj wybrany przez siebie kolorowy obraz (umieść go w folderze projektu lub podaj ścieżkę absolutną do niego), przekonwertuj go do skali szarości, a następnie wykonaj na nim operację progowania binarnego i wyświetl rezultat.
UWAGA Program z poniższych kroków powinien działać w czasie rzeczywistym, tj. zmiana jednego z suwaków powoduje zmianę wyświetlanego obrazu wynikowego i nie blokuje wyświetlania obrazu. Wykorzystaj do tego nieskończoną pętlę.
Zmodyfikuj powyższy program tak, aby korzystał z poznanego wcześniej trackbaru (
cv2.createTrackbar
), a wskazywane przez niego wartości zostały użyte jako wartość progowania.Dodaj kolejny suwak, który umożliwi wybór typu progowania.
Zmiana rozmiaru - skalowanie obrazów
Obraz jest dwuwymiarową funkcją dyskretną. Po zastosowaniu powyższych przekształceń część pikseli jest przekształcana w niecałkowite współrzędne, a część pikseli nowego obrazu nie ma określonej wartości. Aby ją wyznaczyć konieczne jest zastosowanie interpolacji. Istnieje wiele metoda, z których omówione zostaną tylko najpopularniejsze.
Najbliższy sąsiad (nearest neighbor)
Jest to najszybsza metoda interpolacji - pikselowi przypisywana jest wartość najbliższego znanego piksela.
Biliniowa (bilinear)
Jest to metoda dająca lepsze rezultaty, lecz wymagająca więcej obliczeń. Piksel wypełniany jest średnią ważoną wartości czterech najbliższych pikseli. Wagi zależą od odległości poszczególnych pikseli od analizowanego punktu.
Obszarowa (area)
Jest to metoda szczególnie przydatna przy zmniejszaniu obrazu. Analizowany piksel ma przypisywaną wartość średniej ważonej oryginalnych pikseli, na których się znajduje. Wagi zależą od pola fragmentów analizowanego piskela wspólnego z oryginalnymi pikselami. W przypadku powiększania obrazu metoda działa analogicznie do metody najbliższego sąsiada.
Bikubiczna (bicubic)
Zbliżona do metody biliniowej, bierze pod uwagę sąsiedztwo 4x4 pikseli, w efekcie zwracając średnią ważoną odległościami 16 najbliższych pikseli.
Duże obrazy często są trudne do wyświetlenia i przeanalizowania wykonanych operacji, a dodatkowo czas ich przetwarzania jest długi (obraz o rozmiarze 12 megapixeli, np. 4000 x 3000, zapisany bez kompresji w formacie RGB to około 36 MB!). Zapoznać się z funkcją cv2.resize
: https://docs.opencv.org/5.x/da/d54/group__imgproc__transform.html#ga47a974309e9102f5f08231edc7e7529d.
UWAGA
Zwrócić uwagę na wykluczające się argumenty funkcji
dsize
orazfx
,fy
. W języku Python argumenty opcjonalne podaje się podając ich nazwy.Wywołanie funkcji skalującej do konkretnej rozdzielczości (parametr
dsize
), powinno wyglądać następująco:
img_scaled = cv2.resize(img_to_scale, dsize=(500, 500))
Natomiast w przypadku argumentów
fx
orazfy
, które pozwalają skalować proporcjonalnie, parametrdsize
, który jest wymagany podajemy jako wartość(0, 0)
lubNone
:
Img_scaled = cv2.resize(img_to_scale, (0, 0), fx=0.5, fy=0.5)
💥 Zadanie do wykonania 💥
- Pobierz obraz sześcianu. Wczytaj go i przeskaluj go “w górę”, zwiększając jego rozdzielczość 2.75x. Nie korzystając z trackbara, wypróbuj różne metodami interpolacji (
cv2.INTER_LINEAR
,cv2.INTER_NEAREST
,cv2.INTER_AREA
,cv2.INTER_LANCZOS4
) i wyświetl je w osobnych oknach. Zwróć uwagę na różnice w jakości obrazu.
Obrazy jako macierze - operacje matematyczne
Wszystkie wczytane obrazy przechowywane są jako tablice, na których możliwe jest wykonywanie operacji matematycznych na zasadzie element-wise (tj. odpowiedni piksel z odpowiadającym mu innym). Zapoznaj się z: https://docs.opencv.org/5.x/d0/d86/tutorial_py_image_arithmetics.html.
💥 Zadanie do wykonania 💥
- Wykonaj operację mieszania dwóch obrazów (blending). Wykorzystaj wybrany przez siebie obraz oraz logo Laboratorium Systemów Wizyjnych. Skorzystaj z trackbaru do określenia wartości parametrów
alpha
orazbeta
. Sprawdź, co oznacza saturated operation.
UWAGA Zwróć uwagę, że rozmiary obrazów muszą być takie same, aby można było je dodawać. W przypadku, gdy obrazy mają różne rozmiary, należy je skalować lub przyciąć.
Zadania do samodzielnej realizacji
💥 Zadanie do wykonania 💥
Wykonywanie wielu operacji na tak dużych macierzach jak obrazy może być czasochłonne. W celu wyznaczenia czasu trwania fragmentu programu można skorzystać z dedykowanych do tego funkcji. Zapoznać się z: https://docs.python.org/3/library/time.html#time.perf_counter oraz zbadaj, jak zmienia się czas skalowania obrazu w zależności od użytej interpolacji.
💥 Zadanie do wykonania 💥
Zapoznaj się z adaptacyjnym progowaniem (sekcja Adaptive thresholdng): https://docs.opencv.org/5.x/d7/d4d/tutorial_py_thresholding.html.
💥 Zadanie do wykonania 💥
Bazując na: https://docs.opencv.org/5.x/d0/d86/tutorial_py_image_arithmetics.html, napisz funkcję wyznaczającą negatyw obrazu.